#include "d3d.h"
#include "..\common\util.h"
#include "d3d_texture.h"
#include "..\engine\error.h"
#include "..\engine\engine.h"

Texture::Texture(CD3D9Device* const pDevice) : IResource(pDevice)
{
	m_pDataD3D = NULL; 
	m_IsValid = false;
}

LPDIRECT3DBASETEXTURE9 Texture::DataD3D()
{
	return m_pDataD3D; 
}


D3DTexture2D::D3DTexture2D(CD3D9Device* const pDevice) : Texture(pDevice)
{
	memset(&textureInfo,0,sizeof(D3DXIMAGE_INFO)); 
}

string D3DTexture2D::GetType() const
{
	return string("D3DTexture2D"); 
}

HRESULT	D3DTexture2D::Create(const string &szFilename)
{
	HRESULT hr;
	string filepath;
	LPDIRECT3DBASETEXTURE9 pTextureData = NULL;
	D3DXIMAGE_INFO	info; 

	do{
		if(!szFilename.IsValid()) {
			hr = E_INVALIDARG;
			break;
		}

		filepath = CEngine::GetInstance()->FindFilepath(szFilename);
		if(FAILED(D3DXCreateTextureFromFileEx(GetDevice()->GetDevice(),filepath.c_str(),
				D3DX_DEFAULT,D3DX_DEFAULT,
				D3DX_DEFAULT,0,D3DFMT_FROM_FILE,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR,
				D3DX_FILTER_LINEAR,0,&info,NULL,(LPDIRECT3DTEXTURE9*) &pTextureData))) {
			hr = E_FAIL;
			break;
		}

		if(info.ResourceType != D3DRTYPE_TEXTURE) {
			hr = E_UNEXPECTED;
			break;
		}

		if(m_pDataD3D || m_IsValid || m_Identity.IsValid()) {
			if(FAILED(Release())) {
				hr = E_FAIL;
				break;
			}
		}

		textureInfo = info;
		m_Identity = szFilename; 		
		m_IsValid = true; 
		m_pDataD3D = pTextureData;

		return S_OK; 
	}while(false);
	
	if(pTextureData) {
		pTextureData->Release();
		pTextureData = NULL;
	}
	return hr;
}

/**************************************
 *
 **************************************/
HRESULT	D3DTexture2D::Reload(void)
{
	HRESULT hr;
	LPDIRECT3DBASETEXTURE9 pData = NULL;

	do {
		if(!m_Identity.IsValid()) {
			hr = E_UNEXPECTED;
			break;
		}
		
		string filename = CEngine::GetInstance()->FindFilepath(m_Identity);
		if(FAILED(D3DXCreateTextureFromFileEx(GetDevice()->GetDevice(),filename.c_str(),D3DX_DEFAULT,D3DX_DEFAULT,
				D3DX_DEFAULT,0,D3DFMT_FROM_FILE,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR,
				D3DX_FILTER_LINEAR,0,&textureInfo,NULL,(LPDIRECT3DTEXTURE9*)&pData))) {
			hr = E_FAIL;
			break;
		}

		if(FAILED(m_pDataD3D->Release())) {
			hr = E_FAIL;
			break;
		}

		m_IsValid = true; 
		m_pDataD3D = pData;
		return S_OK; 
	
	}while(false);

	if(pData) {
		pData->Release();
		pData = NULL;
	}

	if(m_pDataD3D)
		m_IsValid = true;
	else
		m_IsValid = false;
	return hr;
}
/**************************************
 *
 **************************************/
Vector2f D3DTexture2D::GetDimensions() {
	return Vector2f(textureInfo.Width,textureInfo.Height);
}

HRESULT	D3DTexture2D::Release(void)
{
	try{
		m_IsValid = false; 
		GetDevice()->DeregisterResource(this); 
		if(m_pDataD3D)
			if(FAILED(m_pDataD3D->Release()))
				throw EE_EXCEPTION("Failed to Release texture object"); 
	
		memset(&textureInfo,0,sizeof(textureInfo)); 
		m_Identity.empty(); 
		EE_ZEROMEMORY(&textureInfo,sizeof(D3DXIMAGE_INFO)); 
	}catch(CError::CErrorData error)
	{
		CError::CError(error, ERRORREPORT_LOG);
		return E_FAIL; 
	}

	return S_OK; 
}
//////////////////////////////
D3DAnimatedTexture2D::D3DAnimatedTexture2D(CD3D9Device* const pDevice) : Texture(pDevice)
{
	m_FrameCount = 0;
	m_FrameRate = 1.0f;
}

string D3DAnimatedTexture2D::GetType() const
{
	return "D3DAnimatedTexture2D"; 
}

HRESULT D3DAnimatedTexture2D::CreateTextureFrames(const string *pFilenames, const int frameCount, const float frameRate)
{
	for(int i = 0; i < frameCount;i++)
	{
		Texture* pFrame = NULL;
		if(SUCCEEDED(GetDevice()->CreateTexture(pFilenames[i],&pFrame)))
		{
			frames.Add(pFrame);
		}

	}

	
	m_FrameCount = frames.Count();

	if(m_FrameCount == 0)
		return E_FAIL;

	m_FrameRate = frameRate;
	m_Identity = "Anim";
	m_Identity.concat(pFilenames[0]);
	m_IsValid = true;
	string name = string("Anim");
	name.concat(pFilenames[0]);

	return S_OK;
}

LPDIRECT3DBASETEXTURE9 D3DAnimatedTexture2D::DataD3D()
{
	const float engineTime = CEngine::GetInstance()->GetTimeDuration();
	Texture *pFrame = NULL;
	int frameIdx = (int)(engineTime * m_FrameRate);

	if(frameIdx < 0)
	{
		frameIdx = frameIdx + ((int)ceilf(-((float)frameIdx / (float)m_FrameCount)) * m_FrameCount);
	}
	else
	{
		frameIdx %= m_FrameCount;
	}

	pFrame = frames[frameIdx];
	return pFrame->DataD3D();
}

HRESULT	D3DAnimatedTexture2D::Create(const string &szFilename)
{
	CError::CError("Error: Incorrect use of texture object.", ERRORREPORT_LOG);
	return E_FAIL;
}

/**************************************
 *
 **************************************/
HRESULT	D3DAnimatedTexture2D::Reload(void)
{
	//Because this object wraps other objects that will discretly
	//reload there is no need for this object to reload them
	return S_OK;
}

HRESULT	D3DAnimatedTexture2D::Release(void)
{
	m_IsValid = false; 
	GetDevice()->DeregisterResource(this); 
	//The render engine will take care of releaseing the
	//textures/frames
	frames.Empty();
	m_Identity.empty(); 
	return S_OK; 
}
//////////////////////////
string D3DTexture3D::GetType() const
{
	return "D3DTexture3D"; 
}
D3DTexture3D::D3DTexture3D(CD3D9Device* const pDevice) : Texture(pDevice)
{
	memset(&textureInfo,0,sizeof(D3DXIMAGE_INFO)); 
	m_pDataD3D = NULL; 
}

HRESULT	D3DTexture3D::Create(const string &szFilename)
{
	HRESULT hr;
	string filepath;
	LPDIRECT3DBASETEXTURE9 pTextureData = NULL;

	do{
		if(!szFilename.IsValid()) {
			hr = E_INVALIDARG;
			break;
		}

		filepath = CEngine::GetInstance()->FindFilepath(szFilename);
		if(FAILED(D3DXCreateCubeTextureFromFileEx(GetDevice()->GetDevice(),filepath.c_str(),D3DX_DEFAULT,
				D3DX_DEFAULT,0,D3DFMT_FROM_FILE,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR,
				D3DX_FILTER_LINEAR,0,&textureInfo,NULL,(LPDIRECT3DCUBETEXTURE9*)&pTextureData))) {
			hr = E_FAIL;
			break;
		}

		if(textureInfo.ResourceType != D3DRTYPE_CUBETEXTURE) {
			hr = E_UNEXPECTED;
			break;
		}

		if(FAILED(Release())) {
			hr = E_FAIL;
			break;
		}

		m_Identity = szFilename; 		
		m_IsValid = true; 
		m_pDataD3D = pTextureData;

		return S_OK; 
	}while(false);
	
	if(pTextureData) {
		pTextureData->Release();
		pTextureData = NULL;
	}
	return hr;
}

/**************************************
 *
 **************************************/
HRESULT	D3DTexture3D::Reload(void)
{
	HRESULT hr;
	LPDIRECT3DBASETEXTURE9 pData = NULL;

	do {
		if(!m_Identity.IsValid()) {
			hr = E_UNEXPECTED;
			break;
		}
		
		string filename = CEngine::GetInstance()->FindFilepath(m_Identity);
		if(FAILED(D3DXCreateCubeTextureFromFileEx(GetDevice()->GetDevice(),filename.c_str(),D3DX_DEFAULT,
				D3DX_DEFAULT,0,D3DFMT_FROM_FILE,D3DPOOL_MANAGED,D3DX_FILTER_LINEAR,
				D3DX_FILTER_LINEAR,0,&textureInfo,NULL,(LPDIRECT3DCUBETEXTURE9*)&pData))) {
			hr = E_FAIL;
			break;
		}

		if(FAILED(m_pDataD3D->Release())) {
			hr = E_FAIL;
			break;
		}

		m_IsValid = true; 
		m_pDataD3D = pData;
		return S_OK; 
	
	}while(false);

	if(pData) {
		pData->Release();
		pData = NULL;
	}

	if(m_pDataD3D)
		m_IsValid = true;
	else
		m_IsValid = false;
	return hr;
 
}
HRESULT	D3DTexture3D::Release(void)
{
	try{
		m_IsValid = false; 
		GetDevice()->DeregisterResource(this); 

		if(m_pDataD3D)
			if(FAILED(m_pDataD3D->Release()))
				throw EE_EXCEPTION("Failed to Release texture object"); 
	
		m_Identity = ""; 
		EE_ZEROMEMORY(&textureInfo,sizeof(D3DXIMAGE_INFO)); 
	}catch(CError::CErrorData error)
	{
		CError::CError(error, ERRORREPORT_LOG);
		return E_FAIL; 
	}

	return S_OK; 
}

HRESULT	CD3D9Device::createCubemapTexture(const string &sz_filename, Texture **texturePtr)
{
	string textureFilename; 
	const string textureName = CUtil::getRemoveFilepath(sz_filename); 
	D3DTexture3D *pNewTexture = NULL; 

	try{
		Texture* pFindTexture = NULL; 

		if(sz_filename.length() == 0)
			return E_FAIL; 

		if(SUCCEEDED(SearchForResource(textureName, (IResource**) &pFindTexture, RESOURCE_TEXTURE)))
		{
			*texturePtr = pFindTexture; 
			return S_OK; 
		}

	const string a(sz_filename.c_str()); 
	textureFilename = CEngine::GetInstance()->FindFilepath(a).c_str(); 
	pNewTexture = EE_NEW D3DTexture3D(this); 
	if(!pNewTexture)
		throw EE_EXCEPTION("Could not allocate EE_NEW texture"); 

	if(FAILED(pNewTexture->Create(textureFilename)))
		throw EE_EXCEPTION("Could not Create texture"); 

	//pNewTexture->m_Identity = textureName; 
	*texturePtr = pNewTexture; 
	m_Textures.Add(pNewTexture); 

	}catch(CError::CErrorData error)
	{
		CError::CError(error); 
		EE_SAFEDELETE(pNewTexture); 
		return E_FAIL; 
	}
	return S_OK; 
}

HRESULT CD3D9Device::CreateTexture(const string &sz_filename, Texture **texturePtr)
{
	HRESULT hr;
	string filename;
	Texture* pCache = NULL; 
	Texture *pNewTexture = NULL; 
	D3DXIMAGE_INFO textureInfo; 

	do{

		if(ISNULL(texturePtr) || !sz_filename.IsValid()) {
			hr = E_INVALIDARG;
			break;
		}

		filename = CUtil::getRemoveFilepath(sz_filename); 
		if(SUCCEEDED(SearchForResource(filename, (IResource**) &pCache, RESOURCE_TEXTURE))) {
			*texturePtr = pCache; 
			return S_OK; 
		}

		string filepath = CEngine::GetInstance()->FindFilepath(sz_filename); 
		if(FAILED(D3DXGetImageInfoFromFile(filepath.c_str(), &textureInfo))) {
			hr = E_FAIL;
			break;
		}

		switch(textureInfo.ResourceType) 
		{
			case D3DRTYPE_CUBETEXTURE: 
				{
					pNewTexture = EE_NEW D3DTexture3D(this); 
				}break; 
			case D3DRTYPE_TEXTURE: 
				{
					pNewTexture = EE_NEW D3DTexture2D(this); 
				}break; 
		}
	
		if(ISNULL(pNewTexture)) {
			hr = E_OUTOFMEMORY;
			break;
		}

		if(FAILED(pNewTexture->Create(sz_filename))) {
			hr = E_FAIL;
			break;
		}

		*texturePtr = pNewTexture; 
		m_Textures.Add(pNewTexture); 
		return S_OK; 
	}while(false);

	DeregisterResource(pNewTexture);
	EE_SAFEDELETE(pNewTexture);

	return hr; 
}

HRESULT CD3D9Device::CreateTexture(const string *pFilenames, const int frameCount, const float frameRate, Texture **texturePtr)
{
	D3DAnimatedTexture2D* pNewTexture = NULL; 

	try{
		if(ISNULL(pFilenames) || frameCount == 0 || ISNULL(texturePtr))
			throw EE_EXCEPTION("Bad parameters."); 

		pNewTexture = EE_NEW D3DAnimatedTexture2D(this);
		if(ISNULL(pNewTexture))
			throw EE_EXCEPTION("Could not allocate texture"); 

		if(FAILED(pNewTexture->CreateTextureFrames(pFilenames, frameCount)))
			throw EE_EXCEPTION("Failed to specify frames."); 
	
		*texturePtr = pNewTexture; 
		m_Textures.Add(pNewTexture); 

	}catch(CError::CErrorData error)
	{
		CError::CError(error, ERRORREPORT_LOG); 
		EE_SAFEDELETE(pNewTexture); 
		return E_FAIL; 
	}
	return S_OK; 
}
